package de.dopichaj.labrador.demo;


import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.cli.BasicParser;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.log4j.Logger;
import org.xml.sax.SAXException;

import de.dopichaj.labrador.index.IndexVersionException;
import de.dopichaj.labrador.index.IndexWriter;
import de.dopichaj.labrador.index.lucene.LuceneFileSystemIndex;
import de.dopichaj.labrador.index.own.OwnFileSystemIndex;
import de.dopichaj.labrador.index.selector.AllSelector;
import de.dopichaj.labrador.index.selector.IndexSelector;
import de.dopichaj.labrador.util.Configuration;
/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * This is basically a copy of the lucene demo for indexing files. It has been
 * extended to serve as a front-end for xml indexing.
 * If "-" is given for the root directory, the names of the XML files to index
 * is read from standard input.<br />
 */
public class IndexXMLFiles {

    private static final Logger log = Logger.getLogger(IndexXMLFiles.class);

    private IndexXMLFiles() {
        // This class should not be instantiated.
    }
    
    /**
     * Main class for the command-line client.
     * @param args
     * @throws IndexVersionException 
     */
    public static void main(String[] args) throws IndexVersionException {
        
        Options options = new Options();
        options.addOption("s", "stopwords", true, "stopword file");
        options.addOption("i", "de.dopichaj.labrador.index", true,
            "de.dopichaj.labrador.index directory (defaults to \"de.dopichaj.labrador.index\")");
        options.addOption("c", "create", false,
            "create de.dopichaj.labrador.index (deleting existing de.dopichaj.labrador.index if necessary");
        options.addOption("S", "de.dopichaj.labrador.index-selector", true,
            "a JavaScript fragment to instantiate an IndexSelector");
        options.addOption("C", "configuration", true, "the configuration properties");
        CommandLineParser parser = new BasicParser();
        CommandLine cmd = null;
        try {
            cmd = parser.parse(options, args); 
        } catch (ParseException pe) {
            help(options, "");
        }

        String[] files = cmd.getArgs();
        if (files.length == 0) {
            help(options, "Supply at least one file to de.dopichaj.labrador.index");
        }
        String stopWordFile = cmd.getOptionValue('s');
        boolean create = cmd.hasOption('c');
        if (cmd.hasOption('s') && !create) {
            help(options, "You can only supply the stopword file when " +
                "creating an de.dopichaj.labrador.index");
        } else if (create && !cmd.hasOption('s')) {
            stopWordFile = null;
        }
        File indexDirectory = new File(cmd.getOptionValue('i', "de.dopichaj.labrador.index"));

        // try to create de.dopichaj.labrador.index directory
        try {
            createIndexDirectory(create, indexDirectory);
            
            PrintWriter writer = new PrintWriter(new FileOutputStream(
               new File(indexDirectory, "commandline.txt")));
            for (String arg : args) {
                writer.println(arg);
            }
            writer.close();
        } catch (IOException e1) {
            System.err.println("Error creating de.dopichaj.labrador.index directory: ");
            e1.printStackTrace();
            System.exit(1);
        }
        
        String[] stopWords = null;
        if (create) {
            try {
                stopWords = readStopWordFile(stopWordFile);
            } catch (FileNotFoundException e) {
                System.err.println("Stop word file not found: " + stopWordFile);
                e.printStackTrace();
                System.exit(1);
            } catch (IOException e) {
                System.err.println("Error accessing stop word file " +
                    stopWordFile);
                e.printStackTrace();
                System.exit(1);
            }
        }

        IndexSelector selector = null;
        if (cmd.hasOption('S')) {
            ScriptEngineManager factory = new ScriptEngineManager();
            ScriptEngine engine = factory.getEngineByName("JavaScript");
            
            try {
                selector = (IndexSelector)engine.eval(cmd.getOptionValue('S'));
            } catch (ScriptException e) {
                System.err.println("Error in IndexSelector construction: " + e);
                System.exit(1);
            }
        } else {
            selector = new AllSelector();
        }
        
        Configuration configuration = null;
        if (cmd.hasOption('C')) {
            try {
                configuration = new Configuration(new FileInputStream(cmd.getOptionValue('C')));
            } catch (IOException e) {
                System.err.println("Error reading configuration file: " + e.getMessage());
                System.exit(1);
            }
        } else {
            configuration = new Configuration();
        }
        
        // de.dopichaj.labrador.index the files
        IndexWriter filer = null;
        try {
            if (create) {
                filer = new IndexWriter(
                    new OwnFileSystemIndex(indexDirectory, stopWords, configuration),
                    selector, true);
            } else {
                filer = new IndexWriter(new LuceneFileSystemIndex(indexDirectory),
                    selector, false);
            }
        } catch (ParserConfigurationException e1) {
            e1.printStackTrace();
            System.exit(1);
        } catch (IOException e1) {
            e1.printStackTrace();
            System.exit(1);
        }
        try {
            for (int i = 0; i < files.length; i++) {
                String rootDirectory = files[i];
                if (rootDirectory.equals("-")) {
                    java.io.BufferedReader br = new java.io.BufferedReader(
                            new java.io.InputStreamReader(System.in));
                    String line;

                    while ((line = br.readLine()) != null) {
                        System.out.println("Adding " + line);
                        try {
                            filer.addDocument(new File(line));
                        } catch (SAXException e) {
                            log.error("Error parsing document " + line, e);
                            System.err.println("Error parsing document " + line);
                        }
                    }
                } else {
                    indexDocs(filer, new File(rootDirectory));
                }
            }
            
        } catch (IOException e) {
            System.err.println(" caught a " + e.getClass()
                    + "\n with message: " + e.getMessage());
        } finally {
            try {
                filer.close();
            } catch (IOException e) {
                System.err.println("Error closing filer. Giving up.");
                e.printStackTrace();
            }
        }        
    }

    /**
     * Read the stopword file.
     * @param stopWordFile The file to read the stopwords from (one per line).
     * @return An array with the stopwords.
     * @throws FileNotFoundException
     * @throws IOException
     */
    private static String[] readStopWordFile(String stopWordFile) throws FileNotFoundException, IOException {
        String[] stopWords = new String[0];
        if (stopWordFile != null) {
            // Read/copy stopwords
            InputStream stopWordIS = new FileInputStream(stopWordFile);
            DataInputStream in = new DataInputStream(stopWordIS);
            try {
                byte[] b = new byte[in.available()];
                in.readFully(b);
                in.close();
                String result = new String(b, 0, b.length);
                stopWords = result.split("\n");
            } finally {
                stopWordIS.close();
                in.close();
            }
        }
        return stopWords;
    }

    /**
     * Make sure that the index directory exists (create it if necessary).
     * @param create    Flag whether the directory should be created.
     * @param indexDir  The path of the index directory.
     * @throws IOException
     */
    private static void createIndexDirectory(boolean create, File indexDir) throws IOException {
        if (indexDir.exists()) {
            if (create && indexDir.isDirectory()) {
                System.err.println("Error: trying to create new de.dopichaj.labrador.index, but de.dopichaj.labrador.index directory " + indexDir + " exists.");
                System.exit(1);
            } else if (!indexDir.isDirectory()) {
                throw new IOException(indexDir + " exists, but is no directory");
            }
        } else {
            if (!indexDir.mkdir()) {
                throw new IOException("Could not create de.dopichaj.labrador.index directory " + indexDir);
            }
        }
    }

    /**
     * Called recursively to index all files in a directory and below.
     * @param filer The FileXMLDocument instance to use.
     * @param file The file to index.
     * @throws IOException
     */
    public static void indexDocs(IndexWriter filer, File file)
            throws IOException {
        // do not try to de.dopichaj.labrador.index files that cannot be read
        System.out.println("Trying to find " + file);
        if (file.canRead()) {
            if (file.isDirectory()) {
                String[] files = file.list();
                // an IO error could occur
                if (files != null) {
                    for (int i = 0; i < files.length; i++) {
                        indexDocs(filer, new File(file, files[i]));
                    }
                }
            } else {
                System.out.println("Adding " + file);
                log.info("Adding " + file);
                try {
                    filer.addDocument(file);
                }
                // at least on windows, some temporary files raise this
                // exception with
                // an "access denied" message
                // checking if the file can be read doesn't help
                catch (FileNotFoundException fnfe) {
                    log.warn("File not found: " + file, fnfe);
                } catch (SAXException e) {
                    log.error("Error parsing " + file, e);
                }
            }
        }
    }
    
    private static void help(Options options, String error) {
        HelpFormatter fmt = new HelpFormatter();
        fmt.printHelp("java IndexXMLFiles [-c [-s <stop_word_file>]] " +
            "[-o] [-m <max_inline_len>] [-i <index_directory>] (<file or dir>|-)*",
            "", options, "\n" + error);
        System.exit(1);
    }
}
